package com.og.managedbean;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;

import javax.el.ValueExpression;
import javax.enterprise.context.RequestScoped;

import org.primefaces.component.api.UIColumn;

import javax.faces.component.UIComponent;
import javax.faces.component.ValueHolder;

import org.primefaces.component.datatable.DataTable;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.ServletContext;

import org.apache.log4j.Logger;

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.ExceptionConverter;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontFactory;
import com.itextpdf.text.Header;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.fonts.otf.TableHeader;
import com.og.backingbean.Application;
import com.og.backingbean.Contract;
import com.og.backingbean.Module;
import com.og.backingbean.ModuleHistory;

/**
 * @author Renaud DIANA
 *
 */
@Named
@RequestScoped
public class PdfBean implements Serializable {

	/***************************************** Require itextpdf-5.4.3.jar in classpath *************************************/
	
	private static final long serialVersionUID = 1L;
	
	private DataTable table;
	private String email;
		
	// Log4j	 
	private static final Logger	log	= Logger.getLogger(PdfBean.class);

	/* Dependency injection */	
    // BackingBean    
    @Inject
    private Module module; 
    @Inject
    private ModuleHistory moduleHistory;
    @Inject
    private Application application;
                    
    /* -------------------- */
    
	/**
	 * Default constructor
	 */
	public PdfBean() {
		// TODO Auto-generated constructor stub
	}

	/**
     * Inner class to add a table as header.
     */
    class TableHeader extends PdfPageEventHelper {
        /** The header text. */
        //String header;
        /** The template with the total number of pages. */
        //PdfTemplate total;

        /**
         * Allows us to change the content of the header.
         * @param header The new header String
         
        public void setHeader(String header) {
            this.header = header;
        }*/

        /**
         * Creates the PdfTemplate that will hold the total number of pages.
         * @see com.itextpdf.text.pdf.PdfPageEventHelper#onOpenDocument(
         *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
        
        public void onOpenDocument(PdfWriter writer, Document document) {
            total = writer.getDirectContent().createTemplate(30, 16);
        } */

        /**
         * Adds a header to every page
         * @see com.itextpdf.text.pdf.PdfPageEventHelper#onEndPage(
         *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
         */
        public void onEndPage(PdfWriter writer, Document document) {
        	
        	// Get margins 
        	float cellHeight = document.topMargin();
            float rightMargin = document.rightMargin();
            float leftMargin = document.leftMargin();
            
            // PDF document size      
            Rectangle page = document.getPageSize();

            // Create 3 column table
            PdfPTable footer = new PdfPTable(3);
            footer.setTotalWidth(page.getWidth() - rightMargin - leftMargin);
            footer.setLockedWidth(true);
            footer.setHorizontalAlignment(Element.ALIGN_CENTER);
            
            // Add cells
            PdfPCell c1 = new PdfPCell(new Phrase(
                    "O.G. Rocks", new Font(Font.FontFamily.HELVETICA, 10)));
            c1.setHorizontalAlignment(Element.ALIGN_LEFT);
            c1.setFixedHeight(cellHeight);
            c1.setBorder(PdfPCell.TOP);
            footer.addCell(c1);
            
            PdfPCell c2 = new PdfPCell(new Phrase(
                    "www.rylesoftware.com", new Font(Font.FontFamily.HELVETICA, 10)));
            c2.setHorizontalAlignment(Element.ALIGN_CENTER);
            c2.setFixedHeight(cellHeight);
            c2.setBorder(PdfPCell.TOP);
            footer.addCell(c2);
            
            PdfPCell c3 = new PdfPCell(new Phrase(
                    "Bar Mitzva, choucroute party, ...", new Font(Font.FontFamily.HELVETICA, 10)));
            c3.setHorizontalAlignment(Element.ALIGN_RIGHT);
            c3.setFixedHeight(cellHeight);
            c3.setBorder(PdfPCell.TOP);
            footer.addCell(c3);
      
            // Since the table header is implemented using a PdfPTable, we call
            // WriteSelectedRows(), which requires absolute positions!
            footer.writeSelectedRows(
              0, -1,  // first/last row; -1 flags all write all rows
              leftMargin,      // xPos offset              
              cellHeight, // ** bottom** yPos of the table -> 0 = bottom; page.getHeight() = top 
              writer.getDirectContent()
            );            
        }

        /**
         * Fills out the total number of pages before the document is closed.
         * @see com.itextpdf.text.pdf.PdfPageEventHelper#onCloseDocument(
         *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
         
        public void onCloseDocument(PdfWriter writer, Document document) {
            ColumnText.showTextAligned(total, Element.ALIGN_LEFT,
                    new Phrase(String.valueOf(writer.getPageNumber() - 1)),
                    2, 2, 0);
        }*/
    }
    
	public String processPDF() throws IOException, DocumentException {
		try {
			// Create document
            Document pdf = new Document();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            PdfWriter writer = PdfWriter.getInstance(pdf, baos); 
            TableHeader event = new TableHeader();
            writer.setPageEvent(event);
            pdf.open();
       
            // Set document
            pdf.setPageSize(PageSize.A4.rotate());    
            pdf.newPage();
            //event.setHeader("Hello!");
            		
            // Get contexts
            ServletContext servletContext = (ServletContext)
            		FacesContext.getCurrentInstance().getExternalContext().getContext();
            FacesContext ctx = FacesContext.getCurrentInstance();
    		javax.faces.application.Application app = ctx.getApplication();
            
    		// Date formatter
    		Locale locale = app.getDefaultLocale();
    		DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, locale);
    		
            // Add logo            
    		String logo = servletContext.getRealPath("") + File.separator + "resources" + File.separator + "img" +
    				File.separator + "rsNew.png";
    		Image img = Image.getInstance(logo);
    		img.scaleToFit(200,	55);
    		pdf.add(img);
    			 
    		// Current date    		
    		String dateStr = df.format(new Date());
    		Paragraph date = new Paragraph(dateStr);
    		date.setAlignment(Element.ALIGN_RIGHT);
    		pdf.add(date);

    		pdf.add(Chunk.NEWLINE);
    		
    		// Header
    		Font titleFont = FontFactory.getFont(FontFactory.HELVETICA, Font.DEFAULTSIZE, Font.BOLD);
    		String titleStr = app.evaluateExpressionGet(ctx, "#{i18n.pdfTitle}", String.class) + ": " + application.getName();
    		Paragraph title = new Paragraph(titleStr, titleFont);    		
    		title.setAlignment(Element.ALIGN_CENTER);
    		pdf.add(title);
    		    		    		    		
    		pdf.add(Chunk.NEWLINE);
    		
    		// Set 3 months cancellation
    		Contract contract = application.getListContract().get(0);
    		Calendar cal = GregorianCalendar.getInstance();
    		cal.setTime(contract.getEndDate());
    		cal.add(GregorianCalendar.MONTH, -3);
    		Date cancelDate = cal.getTime();
  
    		// Contract info
    		String contractStr = app.evaluateExpressionGet(ctx, "#{i18n.contract}", String.class);
    		String startDateStr = app.evaluateExpressionGet(ctx, "#{i18n.startDateLabel}", String.class);
    		String cancelDateStr = app.evaluateExpressionGet(ctx, "#{i18n.pdfCancelDate}", String.class);
    		String endDateStr = app.evaluateExpressionGet(ctx, "#{i18n.endDateLabel}", String.class);
    		String emailStr = app.evaluateExpressionGet(ctx, "#{i18n.emailLabel}", String.class);
    		String infoStr = contractStr + ": " + contract.getIdContract() +
    							"\n" + startDateStr + ": " + df.format(contract.getStartDate()) +
    							"\n" + cancelDateStr + ": " + df.format(cancelDate) +
    							"\n" + endDateStr + ": " + df.format(contract.getEndDate()) +
    							"\n" + emailStr + ": " + email;
    							//"\n" + emailStr + ": " + contract.getListCompanies().get(0).getListPersons().get(0).getEmail();
    		Paragraph info = new Paragraph(infoStr);
    		pdf.add(info);
    		
    		pdf.add(Chunk.NEWLINE);
            
    		// Get data from dataTable    		
    		pdf.add(exportAll(FacesContext.getCurrentInstance()));
    		
    		// Footer
    		String endStr = "Fin de table";
    		Paragraph end = new Paragraph(endStr);
    		end.setFont(FontFactory.getFont(FontFactory.HELVETICA));
    		end.setAlignment(Element.ALIGN_CENTER);
    		pdf.add(end);
    		
    		// Close document
            pdf.close();
            
            // Instruct JSF that response is already handling
            FacesContext.getCurrentInstance().responseComplete();
            
            // Send response
            String filename = application.getName();             
            writePDFToResponse(FacesContext.getCurrentInstance().getExternalContext(), baos, filename);
            
        } 
		catch(DocumentException e) {
			throw new IOException(e.getMessage());
        }		
		
		return "";
	}
	
	public PdfPTable exportAll(FacesContext facesContext) {
		   
		// Get dataTable parameters
		int first = table.getFirst();
	    int rowCount = table.getRowCount();
	    int columnsCount = table.getColumnsCount();
        
        PdfPTable pdfTable = new PdfPTable(columnsCount);
        pdfTable.setWidthPercentage(95);
        
        // Columns facets (headers)
        addColumnFacets(facesContext, table, pdfTable);

        // Data rows
        for(int rowIndex = 0; rowIndex < rowCount; rowIndex++) {                
            exportRow(facesContext, pdfTable, rowIndex);
        }
       
        // Restore
        table.setFirst(first);
        
        return pdfTable;
    }
	   
	public void exportRow(FacesContext facesContext, PdfPTable pdfTable, int rowIndex) {
		
        table.setRowIndex(rowIndex);
       
        if(!table.isRowAvailable()) {
            return;
        }
        else {
        	exportCells(facesContext, pdfTable, rowIndex);
        }
    }
   
	public void exportCells(FacesContext facesContext, PdfPTable pdfTable, int rowIndex) {
    	
    	Font cellFont = FontFactory.getFont(FontFactory.HELVETICA);
        
        for(UIColumn col : table.getColumns()) {
            
        	addColumnValue(facesContext, pdfTable, col.getChildren(), cellFont, rowIndex); 
        	
        }
    }
       
	public void addColumnFacets(FacesContext facesContext, DataTable table, PdfPTable pdfTable) {
    	
    	Font facetFont = FontFactory.getFont(FontFactory.HELVETICA, Font.DEFAULTSIZE, Font.BOLD);
    	
        for(UIColumn col : table.getColumns()) {
        	if(col instanceof ValueHolder) {

        		
            }
        	log.debug("Col facet: " + col.getFacet("header"));
        	
        	addColumnValue(facesContext, pdfTable, col.getFacet("header"), facetFont);
        }
    }
       
	// Columns facets (headers)
	public void addColumnValue(FacesContext facesContext, PdfPTable pdfTable, UIComponent component, Font font) {
				
        String value = component == null ? "" : exportValue(facesContext, component); 
        PdfPCell cell = new PdfPCell(new Paragraph(value, font));
        cell.setBackgroundColor(new BaseColor(0x55, 0xC1, 0xED));
        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
        cell.setPaddingBottom(5);
        pdfTable.addCell(cell);
    }
   
	// Data rows
	public void addColumnValue(FacesContext facesContext, PdfPTable pdfTable, List<UIComponent> components, Font font, int rowIndex) {
				
        StringBuilder builder = new StringBuilder();
               
        for(UIComponent component : components) {
            if(component.isRendered() ) {
            	String value = exportValue(facesContext, component);
           
            	if(value != null)
                    builder.append(value);
            }
        }  
       
        PdfPCell cell = new PdfPCell(new Paragraph(builder.toString(), font));
        cell.setPaddingBottom(5);
        if(rowIndex % 2 == 0)
        	cell.setBackgroundColor(new BaseColor(0xC1, 0xC0, 0xBF));
        pdfTable.addCell(cell);
    }

	public String exportValue(FacesContext context, UIComponent component) {

    	ValueHolder valueHolder = (ValueHolder) component;
        Object value = valueHolder.getValue();
        if(value == null)
        	return "";

        // First ask the converter
        if(valueHolder.getConverter() != null) {
            return valueHolder.getConverter().getAsString(context, component, value);
        }
        
        // Try to guess
        else {
            ValueExpression expr = component.getValueExpression("value");
            if(expr != null) {
                Class<?> valueType = expr.getType(context.getELContext());
                if(valueType != null) {
                    Converter converterForType = context.getApplication().createConverter(valueType);

                    if(converterForType != null)
                    	return converterForType.getAsString(context, component, value);
                }
            }
        }

        // No converter found just return the value as string
        return value.toString();
    }
	
	public void writePDFToResponse(ExternalContext externalContext, ByteArrayOutputStream baos, String fileName) 
			throws IOException, DocumentException {
		
        externalContext.setResponseContentType("application/pdf");
        externalContext.setResponseHeader("Expires", "0");
        externalContext.setResponseHeader("Cache-Control","must-revalidate, post-check=0, pre-check=0");
        externalContext.setResponseHeader("Pragma", "public");
        externalContext.setResponseHeader("Content-disposition", "attachment;filename=" + fileName + ".pdf");
        externalContext.setResponseContentLength(baos.size());
        
        OutputStream out = externalContext.getResponseOutputStream();
        baos.writeTo(out);
        out.close();
        externalContext.responseFlushBuffer();
    }

	// Getters and Setters
	
	public Module getModule() {
		return module;
	}

	public void setModule(Module module) {
		this.module = module;
	}

	public ModuleHistory getModuleHistory() {
		return moduleHistory;
	}

	public void setModuleHistory(ModuleHistory moduleHistory) {
		this.moduleHistory = moduleHistory;
	}

	public Application getApplication() {
		return application;
	}

	public void setApplication(Application application) {
		this.application = application;
	}

	public DataTable getTable() {
		return table;
	}

	public void setTable(DataTable table) {
		this.table = table;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

}
